package org.zjt.thread;

import com.alibaba.fastjson.JSON;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

/**
 * Description: 多个操作原子性操作
 *
 * @author juntao.zhang
 * Date: 2018-06-01 下午3:41
 * @see
 */
@Slf4j
public class AtomicMultiOperation {

    private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(20);

    private static final CyclicBarrier CYCLIC_BARRIER = new CyclicBarrier(20);


    public static void main(String[] args) throws InterruptedException {
        MultiOperation multiOperation = new MultiOperation();
        AtomicReference<MultiOperation> atomicReference = new AtomicReference<>(multiOperation);
        CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(20);

        for (int j = 0; j < 10; j++) {
            new Thread(()->{
                for (int i = 0; i < 20000; i++) {
                    MultiOperation multiOperation2 = atomicReference.get().copy();
                    multiOperation2.add(i,i + 1);
                    EXECUTOR_SERVICE.submit(Try.of(() -> CAS(atomicReference,multiOperation2)));
                    atomicReference.get().assertSize();
                }
                COUNT_DOWN_LATCH.countDown();
            }).start();



            new Thread(()->{
                for (int i = 0; i < 10000; i++) {
                    MultiOperation multiOperation2 = atomicReference.get().copy();
                    multiOperation2.remove(i,i + 1);
                    EXECUTOR_SERVICE.submit(Try.of(() -> CAS(atomicReference,multiOperation2)));
                    atomicReference.get().assertSize();
                }
                COUNT_DOWN_LATCH.countDown();
            }).start();

        }
        COUNT_DOWN_LATCH.await();

        EXECUTOR_SERVICE.shutdown();
        atomicReference.get().show();

    }


    /**
     * 对象的CAS 原子操作
     * @param atomicReference
     * @param newValue
     * @throws BrokenBarrierException
     * @throws InterruptedException
     */
    public static void CAS(AtomicReference<MultiOperation> atomicReference,MultiOperation newValue) throws BrokenBarrierException, InterruptedException {
        MultiOperation expect = atomicReference.get();
        CYCLIC_BARRIER.await();
        atomicReference.compareAndSet(expect,newValue);
    }
}


@Slf4j
@ToString
class MultiOperation<T>{

    private CopyOnWriteArrayList<T> list1 = new CopyOnWriteArrayList();
    private CopyOnWriteArrayList<T>  list2 = new CopyOnWriteArrayList();


    public void add(T t1,T t2) {
        list1.add(t1);
        list2.add(t2);
    }


    public void remove(T t1,T t2) {
        list1.remove(t1);
        list2.remove(t2);
    }

    public void assertSize() {
        log.info("list1 :{}  list2 :{}",list1.size(),list2.size());
        Asserts.check(Objects.equals(list1.size(),list2.size()),"size 不一样大");
    }



    public void show() {
        log.info("list1:{} ",JSON.toJSONString(list1));
        log.info("list2:{} ",JSON.toJSONString(list2));
    }


    public MultiOperation copy(){
        CopyOnWriteArrayList<T> newlist1 = new CopyOnWriteArrayList(list1);
        CopyOnWriteArrayList<T>  newlist2 = new CopyOnWriteArrayList(list2);
        MultiOperation multiOperation = new MultiOperation();
        multiOperation.list1 = newlist1 ;
        multiOperation.list2 = newlist2 ;
        return multiOperation;
    }

}
